// exception.cc 
//      Entry point into the Nachos kernel from user programs.
//      There are two kinds of things that can cause control to
//      transfer back to here from user code:
//
//      syscall -- The user code explicitly requests to call a procedure
//      in the Nachos kernel.  Right now, the only function we support is
//      "Halt".
//
//      exceptions -- The user code does something that the CPU can't handle.
//      For instance, accessing memory that doesn't exist, arithmetic errors,
//      etc.  
//
//      Interrupts (which can also cause control to transfer from user
//      code into the Nachos kernel) are handled elsewhere.
//
// For now, this only handles the Halt() system call.
// Everything else core dumps.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "system.h"
#include "syscall.h"
#include "filesys.h"
#include "machine.h"
#include "progtest.h"

//----------------------------------------------------------------------
// UpdatePC : Increments the Program Counter register in order to resume
// the user program immediately after the "syscall" instruction.
//----------------------------------------------------------------------
static void
UpdatePC ()
{
    int pc = machine->ReadRegister (PCReg);
    machine->WriteRegister (PrevPCReg, pc);
    pc = machine->ReadRegister (NextPCReg);
    machine->WriteRegister (PCReg, pc);
    pc += 4;
    machine->WriteRegister (NextPCReg, pc);
}

//----------------------------------------------------------------------
// LaunchProcess
//          Dummy function use to launch StartProcess with the syscall
//          ForkExec
//----------------------------------------------------------------------
void
LaunchProcess(int name){
	  char filename[MAXFILENAMESIZE];
	  readString(name,filename,MAXFILENAMESIZE); // *filename* : name of the specified file
	  StartProcess(filename);
}

//----------------------------------------------------------------------
// ExceptionHandler
//      Entry point into the Nachos kernel.  Called when a user program
//      is executing, and either does a syscall, or generates an addressing
//      or arithmetic exception.
//
//      For system calls, the following is the calling convention:
//
//      system call code -- r2
//              arg1 -- r4
//              arg2 -- r5
//              arg3 -- r6
//              arg4 -- r7
//
//      The result of the system call, if any, must be put back into r2. 
//
// And don't forget to increment the pc before returning. (Or else you'll
// loop making the same system call forever!
//
//      "which" is the kind of exception.  The list of possible exceptions 
//      are in machine.h.
//----------------------------------------------------------------------

void
ExceptionHandler (ExceptionType which)
{
    int type = machine->ReadRegister (2);

    if ((which == SyscallException) && (type == SC_Halt))
      {
	  DEBUG ('a', "Shutdown, initiated by user program.\n");
	  interrupt->Halt ();
      }
    
    // System Call -- YIELD
    else
      if ((which == SyscallException) && (type == SC_Yield))
	{
	  DEBUG ('a', "Put the current thread at the end of the scheduler.\n");

	  // Yiel the current thread
	  currentThread->Yield();
	}

    // System Call -- EXIT
      else if ((which == SyscallException) && (type == SC_Exit))
	{
	  DEBUG ('a', "Finishing the current proccess\n");
	  
	  // Save the status
	  int status = machine->ReadRegister (4);

	  // Save the pid
	  SpaceId pid = machine->FindUserProgId(currentThread);

	  // If pid = -1, the process isn't in the process table
	  if(pid!=-1){
	    machine->RemoveUserProg(pid);
	  }

	  // Return the status
	  machine->WriteRegister(2,status);

	  // Finish the current thread
	  currentThread->Finish();
	}

    // System Call -- CREATE
      else if ((which == SyscallException) && (type == SC_Create))
	{
	  DEBUG ('f',"Create a new file\n");

	  // Find the name of the specified file
	  int name = machine->ReadRegister (4);
	  char filename[MAXFILENAMESIZE];
	  readString(name,filename,MAXFILENAMESIZE); // *filename* : name of the specified file

	  // Create the specified file
	  fileSystem->Create(filename,MAXFILENAMESIZE);
	}

    // System Call -- OPEN
      else if ((which == SyscallException) && (type == SC_Open))
	{	  
	  // Find the name of the specified file
	  int name = machine->ReadRegister (4);
	  char filename[MAXFILENAMESIZE];
	  readString(name,filename,MAXFILENAMESIZE); // *filename* : name of the specified file
	  	  
	  // Find a free place for the file
	  OpenFileId fid = fileSystem->FindFreeOpenFileId();
	  
	  // Update the file system
	  fileSystem->UpdateById(fid,fileSystem->Open(filename),1);
	  
	  // Return the OpenFileId *id*
	  machine->WriteRegister(2,fid);

	  DEBUG('f',"Open the file %d\n",fid);
	}

    // System Call -- READ
      else if ((which == SyscallException) && (type == SC_Read))
	{
	  // Save the parameters of the specified syscall
	  int nbuffer = machine->ReadRegister (4);
	  int size = machine->ReadRegister (5);
	  int fid = machine->ReadRegister (6);

	  // Temporary string
	  char tmp[size];

	  // Apply the method Read on the right open file
	  OpenFile* node = fileSystem->FindNodeById(fid);
	  int status = node->ReadAt(tmp,size,0);

	  // Write the read string in the specified buffer
	  writeString(nbuffer,tmp,size);

	  // Return the status
	  machine->WriteRegister(2,status);

 	  DEBUG('f',"Read the file %d : %d characteres\n",fid,status);	  
	}

    // System Call -- WRITE
      else if ((which == SyscallException) && (type == SC_Write))
	{
	  // Find the name of the specified file
	  int name = machine->ReadRegister (4);
	  int size = machine->ReadRegister (5);
	  char tmp[size];
	  int status;
	  readString(name,tmp,size);
	  
	  // Update the file system
	  OpenFileId fid = machine->ReadRegister(6);
	  OpenFile* node = fileSystem->FindNodeById(fid);
	  status = node->WriteAt(tmp,size,0);

	  DEBUG('f',"Write in the file %d : %d characteres\n",fid,status);	  	  
	}

    // System Call -- CLOSE
      else if ((which == SyscallException) && (type == SC_Close))
	{
	  // Wich file will we close ?
	  OpenFileId fid = machine->ReadRegister(4);
	  
	  // Close the specified open file
	  OpenFile* node = fileSystem->FindNodeById(fid);
	  fileSystem->UpdateById(fid,node,0);
	  delete node;

	  DEBUG ('f',"Close an open file %d\n",fid);
	}

    // System Call -- FORKEXEC
      else if ((which == SyscallException) && (type == SC_ForkExec))
	{	 
	  DEBUG('up',"ForkExec of a user program\n");

	  // Search the specified executable
	  int name = machine->ReadRegister (4);
	  SpaceId pid;

	  // Add a new user program
	  Thread * t = new Thread("user prog",1);
	  pid = machine->AddUserProg (t);  

	  // Return the pid
	  machine->WriteRegister(2,pid);

	  // Fork the function LaunchProcess with his name
	  t->Fork(LaunchProcess,name);
	}  

    // System Call -- JOIN
      else if ((which == SyscallException) && (type == SC_Join))
	{
	  // Fing the user program
	  SpaceId pid = machine->ReadRegister (4);
	  Thread * t;
	  t = machine->FindUserProgThread (pid);
	  // Join the thread
	  t->Join();	  
	}   
    // Not a system call
      else
	{
	  printf ("Unexpected user mode exception %d %d\n", which, type);
	  ASSERT (FALSE);
	}
    
    // LB: Do not forget to increment the pc before returning!
    UpdatePC ();
    // End of addition
}
